package com.lagou.edu.factory.parser;


import com.lagou.edu.factory.BeanDefinitionParser;
import com.lagou.edu.factory.BeanFactory;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.factory.annotation.*;
import org.dom4j.Element;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Author: ZLY
 * @Date: 2020/10/14 22:33
 * @Description: component 标签解析类
 */
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {

    @Override
    public void parse(List<Element> elements, Map<String, Object> beanMap) throws Exception {
        //只读取第一个匹配的 <component-scan base-package="com.lagou.edu"/>
        if (elements != null && elements.size() > 0) {
            Element element = elements.get(0);
            //获取包路径
            String basePackage = element.attributeValue("base-package").trim();

            //得到改包下的所有class
            Set<Class<?>> allClass = new ClassPathBeanDefinitionScanner().scan(basePackage);
            //bean扫描到容器中
            scanBean(allClass,beanMap);
            //property注入
            scanProperty(allClass,beanMap);
            //Transactional事务扫描
            scanTransaction(allClass,beanMap);

        }

    }

    /**
     * 扫描事务相关注解
     * @param allClass
     * @param beanMap
     */
    private void scanTransaction(Set<Class<?>> allClass, Map<String, Object> beanMap) {
        for (Class<?> aClass : allClass) {
            if (aClass.getAnnotation(Transactional.class) !=null) {
                //如果该类标注了事务,则为该类产生代理类
                String beanName = getBeanName(aClass);
                ProxyFactory proxyFactory = (ProxyFactory)beanMap.get(BeanFactory.TRANSACTION_BEAN_NAME);

                //如果该类继承了接口则采用jdk动态代理,否则为cglib
                Class<?>[] interfaces = aClass.getInterfaces();
                Object oldBean = beanMap.get(beanName);
                Object proxyBean = null;
                if(interfaces!=null && interfaces.length > 0){
                    proxyBean = proxyFactory.getJdkProxy(oldBean);
                }else{
                    proxyBean = proxyFactory.getCglibProxy(oldBean);
                }
                beanMap.put(beanName,proxyBean);
            }
        }
    }

    /**
     * 扫描 @AutoWired注解,并把相应的bean注入
     * @param allClass
     * @param beanMap
     */
    private void scanProperty(Set<Class<?>> allClass, Map<String, Object> beanMap) throws IllegalAccessException {
        for (Class<?> aClass : allClass) {
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields) {
                if(field.isAnnotationPresent(Autowired.class)){
                    //暴力访问
                    field.setAccessible(true);
                    //父 bean name eg:xxxService
                    String fatherBeanName = getBeanName(aClass);
                    Object fatherObj = beanMap.get(fatherBeanName);
                    //子 bean name eg:xxxDao
                    String sonBeanName = getBeanName(field.getType());
                    Object sonObj = beanMap.get(sonBeanName);

                    //注入值
                    field.set(fatherObj,sonObj);

                    beanMap.put(fatherBeanName,fatherObj);
                }
            }
        }
    }

    /**
     * 扫描Component,Repository,Service注解,并放入ioc容器中
     * @param allClass
     * @param beanMap
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void scanBean(Set<Class<?>> allClass, Map<String, Object> beanMap) throws IllegalAccessException, InstantiationException {
        for (Class<?> aClass : allClass) {
            if (aClass.getAnnotation(Component.class) == null &&
                    aClass.getAnnotation(Repository.class) == null &&
                    aClass.getAnnotation(Service.class) == null) {
                continue;
            }
            //从注解获取beanName
            String beanName = getBeanName(aClass);
            //已有这个bean则不覆盖
            if (beanMap.containsKey(beanName)) {
                continue;
            }
            beanMap.put(beanName, aClass.newInstance());

        }
    }

    /**
     * 获取bean的名称 有设置则按设置的名称,没有则根据类名生成
     * @param aClass
     * @return
     */
    private String getBeanName(Class<?> aClass) {
        String beanName = getAnnotionBeanName(aClass);
        //如果没有设置beanName则取默认名称
        if (beanName == null||beanName.trim().length()<=0) {
            beanName = getDefaultBeanName(aClass.getSimpleName());
        }
        return beanName;
    }

    private String getAnnotionBeanName(Class<?> aClass) {
        String beanName = null;
        if(aClass.getAnnotation(Component.class)!=null){
            beanName = aClass.getAnnotation(Component.class).value();
        }
        if(beanName==null && aClass.getAnnotation(Service.class)!=null){
            beanName = aClass.getAnnotation(Service.class).value();
        }
        if(beanName==null && aClass.getAnnotation(Repository.class)!=null){
            beanName = aClass.getAnnotation(Repository.class).value();
        }
        return beanName;
    }

    /**
     * 默认是类名的驼峰命名法
     * @return
     */
    private String getDefaultBeanName(String ClassName) {
        char[]chars = ClassName.toCharArray();

        chars[0] += 32;

        return String.valueOf(chars);
    }


}
